/**
 * Projekt do předmetu GJA - LATEX TABLE CREATOR
 *
 * Soubor: RowHeaderTableUI.java
 *
 * @author Ondřej Kubal  <xkluba00@stud.fit.vutbr.cz>
 * @author Petr Šimon    <xsimon10@stud.fit.vutbr.cz>
 */

package table;

import java.awt.*;
import java.awt.event.*;
import javax.swing.event.*;

import javax.swing.plaf.basic.*;
import javax.swing.*;
import javax.swing.table.*;

/**
 *  Takzvané Row Header UI.
 *  Jedná se o přepsaný BasicTableHeaderUI pro potřeby BasicTableUI
 *  Umožňuje vertikální změnu velikosti Buněk v mainTable.
 *  Přidáno nastavování výšky řádků pomocí dialogu.
 *  Spřažená tabulka se nastaví pomocí setMainTable.
 */
public class RowHeaderTableUI extends BasicTableUI
{

     //** Nastaví kurzor!! */
     private static Cursor resizeCursor = Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR);
     
     //** Akuální řádek, na kterém je myš */
     private int rolloverRow = -1;
     
     //** Vybraný řádek */
     private int selectedRowIndex = 0;
     
     //** Table Header property (proměnné, které byly v tableheaderu - nebudeme předělávat další třídu, vytáhneme si je sem !) */
     int ResizingRow;
     int DraggedRow;
     
     //** pro svázání s další hlavní tabulkou */
     private JTable mainTable = null;
     
     /**
      * Nastaví hlavní tabulku
      * @param table
      */
     public  void setMainTable(JTable table)
     {
          mainTable = table;
     }
     
     //** obsluha myši */
     public class MouseInputHandler implements MouseInputListener
     {
     
          //** offset ve vertikálním směru */
          private int mouseYOffset;
          
          //** kurzor */
          private Cursor otherCursor = resizeCursor;
          
          /**
           * kliknutí myši
           * @param e událost myši
           */
          public void mouseClicked(MouseEvent e)
          {
               if (!table.isEnabled())   //povolená tabulka
               {
                    return;
               }
               
               if (e.getClickCount() % 2 == 1 && //1x click
                         SwingUtilities.isLeftMouseButton(e))   //levé talčítko
               {
               
                    //nastavení výběru
                    table.setRowSelectionInterval(table.rowAtPoint(e.getPoint()), table.rowAtPoint(e.getPoint()));
                    
                    if (mainTable != null)
                    {
                         mainTable.selectAll();
                         mainTable.setRowSelectionInterval(table.rowAtPoint(e.getPoint()), table.rowAtPoint(e.getPoint()));
                         mainTable.requestFocus();
                    }
                    
                    RowSorter sorter;
                    
                    if (table != null && (sorter = table.getRowSorter()) != null)
                    {
                         int rowIndex = table.rowAtPoint(e.getPoint());
                         
                         if (rowIndex != -1)
                         {
                              rowIndex = table.convertRowIndexToModel(
                                              rowIndex);
                              sorter.toggleSortOrder(rowIndex);
                         }
                    }
               }
               else if (SwingUtilities.isLeftMouseButton(e))   //levé talčítko //2x click
               {
                    //resize dialog
                    SizeDialog sizeDialog = new SizeDialog(null, true);
                    sizeDialog.setModeWidths(false);
                    sizeDialog.setTable((MultiSpanCellTable) mainTable);
                    sizeDialog.setRowHeader(table);
                    sizeDialog.setIndex(table.rowAtPoint(e.getPoint()));
                    sizeDialog.centerScreen();
               }
          }
          
          /**
           * Vrátí INDEX řádku dle pozice myši
           * @param p  pozice myši
           * @return index
           */
          private int getResizingRow(Point p)
          {
               return getResizingRow(p, table.rowAtPoint(p));
          }
          
          /**
           * Vrátí INDEX řádku dle pozice myši
           * @param p  pozice myši
           * @param  row vybraný řádek
           * @return index
           */
          private int getResizingRow(Point p, int row)
          {
               if (row == -1)
               {
                    return -1;
               }
               
               Rectangle r = table.getCellRect(row, 0, false);  //Jedná se o header -> uvažujeme pouze 0 sloupec tabulky
               r.grow(0, -3); //oblast zmenšená o 3px v horizontálním směru
               
               if (r.contains(p))
               {
                    return -1;
               }
               
               int midPoint = r.y + r.height / 2; //vertikální střed
               int rowIndex;
               
               //těžko říct, zda je to ZCELA správně i ve vertikálním směru místo horizontáního?
               if (table.getComponentOrientation().isLeftToRight())
               {
                    rowIndex = (p.y < midPoint) ? row - 1 : row;
               }
               else
               {
                    rowIndex = (p.y < midPoint) ? row : row - 1;
               }
               
               if (rowIndex == -1)
               {
                    return -1;
               }
               
               return rowIndex; //INDEX řádeku !!!
          }
          
          /**
           * myš je stisknutá - tlačítko dole
           * @param e událost myši
           */
          public void mousePressed(MouseEvent e)
          {
               if (!table.isEnabled())
               {
                    return;
               }
               
               DraggedRow = -1;
               ResizingRow = -1;
               //přetahování neimplementováno
               //table.setDraggedDistance(0);
               
               Point p = e.getPoint(); //akruální pozice myši
               
               int index = table.rowAtPoint(p);//pozice -> řádek
               
               if (index != -1)
               {
                    // The last 3 pixels + 3 pixels of next column are for resizing
                    int resizingRow = getResizingRow(p, index);
                    
                    if (canResize(resizingRow, table))
                    {
                         ResizingRow = resizingRow; //poznačíme si řádek
                         
                         //těžko říct, zda je to ZCELA správně i ve vertikálním směru místo horizontáního?
                         if (table.getComponentOrientation().isLeftToRight())
                         {
                              mouseYOffset = p.y - table.getRowHeight(resizingRow);
                         }
                         else
                         {
                              mouseYOffset = p.y + table.getRowHeight(resizingRow);
                         }
                    }
                    else DraggedRow = index; //začátek výběru
                    
                    ///Reordering nebudeme implementovat
               }
               
               ///Reordering nebudeme implementovat
          }
          
          /**
           * Výměna kurzoru
           * Resizing <-> klasický
           */
          private void swapCursor()
          {
               Cursor tmp = table.getCursor();
               table.setCursor(otherCursor);
               otherCursor = tmp;
          }
          
          /**
           * posunotí myši
           * @param e událost myši
           */
          public void mouseMoved(MouseEvent e)
          {
               //test na povolení tabulky
               if (!table.isEnabled())
               {
                    return;
               }
               
               
               int index = table.rowAtPoint(e.getPoint());//pozice -> řádek
               
               if (index != -1)
               {
               
                    //změna kurzoru dle oblasti nad kterou se nachází kurzor
                    if (canResize(getResizingRow(e.getPoint()), table) !=
                              (table.getCursor() == resizeCursor))
                    {
                         swapCursor();
                    }
                    
               }
               
               updateRolloverRow(e);
          }
          
          /**
           * ukončeno táhnutí myši
           * @param e událost myši
           */
          public void mouseDragged(MouseEvent e)
          {
               if (!table.isEnabled())
               {
                    return;
               }
               
               int mouseY = e.getY();
               
               
               int resizingRow  = ResizingRow;
               //int draggedRow  =  DraggedRow;
               
               boolean tableLeftToRight = table.getComponentOrientation().isLeftToRight();
               
               //změna velikosti řádku
               if (resizingRow >= 0)
               {
                    int newHeight;
                    
                    if (tableLeftToRight)
                    {
                         newHeight = mouseY - mouseYOffset;
                    }
                    else
                    {
                         newHeight = mouseYOffset - mouseY;
                    }
                    
                    mouseYOffset += changeRowHeight(resizingRow, newHeight);
               }
               else if ((DraggedRow != -1) && (table.rowAtPoint(e.getPoint()) != -1))   //označování
               {
                    table.setRowSelectionInterval(DraggedRow, table.rowAtPoint(e.getPoint()));
                    
                    if (mainTable != null)
                    {
                         mainTable.selectAll();
                         mainTable.setRowSelectionInterval(DraggedRow, table.rowAtPoint(e.getPoint()));
                         mainTable.requestFocus();
                    }
               }
               
               // Přesunování řádků neimplementováno
               
               updateRolloverRow(e);
          }
          
          /**
           * uvolněné tlačítko
           * @param e událost myši
           */
          public void mouseReleased(MouseEvent e)
          {
               if (!table.isEnabled())
               {
                    return;
               }
               
               //setDraggedDistance(0, viewIndexForRow(DraggedRow));
               
               ResizingRow = -1;
               DraggedRow = -1;
               
               updateRolloverRow(e);
          }
          
          /**
           * myš vstoupila do oblasti
           * @param e událost myši
           */
          public void mouseEntered(MouseEvent e)
          {
               if (!table.isEnabled())
               {
                    return;
               }
               
               updateRolloverRow(e);
          }
          
          /**
            * myš opustila oblast
            * @param e událost myši
            */
          public void mouseExited(MouseEvent e)
          {
               if (!table.isEnabled())
               {
                    return;
               }
               
               int oldRolloverRow = rolloverRow;
               rolloverRow = -1;
               rolloverRowUpdated(oldRolloverRow, rolloverRow);
          }
     }
     
     /**
       * Budeme používat náš handler myši
       */
     @Override protected MouseInputListener createMouseInputListener()
     {
          return new MouseInputHandler();
     }
     
     /**
       * Zjistí aktuální řádek, na kterém je myš
       */
     protected int getRolloverRow()
     {
          return rolloverRow;
     }
     
     /**
     * událost vyvolávaná při přejezdu myši
     */
     protected void rolloverRowUpdated(int oldRow, int newRow)
     {
     }
     
     /**
     * nastaví aktuální řádek, na kterém je myš
     * @param e událost myši
     */
     private void updateRolloverRow(MouseEvent e)
     {
          if ( //table.getDraggedRow() == null &&
               table.contains(e.getPoint()))
          {
          
               int row = table.rowAtPoint(e.getPoint());
               
               if (row != rolloverRow)
               {
                    int oldRolloverColumn = rolloverRow;
                    rolloverRow = row;
                    rolloverRowUpdated(oldRolloverColumn, rolloverRow);
               }
          }
     }
     
     /**
       * Vybere další řádek
       */
     private int selectNextRow(boolean doIt)
     {
          int newIndex = getSelectedRowIndex();
          
          if (newIndex < table.getRowCount() - 1)
          {
               newIndex++;
               
               if (doIt)
               {
                    selectRow(newIndex);
               }
          }
          
          return newIndex;
     }
     
     /**
     * Vybere předchozí řádek
     */
     private int selectPreviousRow(boolean doIt)
     {
          int newIndex = getSelectedRowIndex();
          
          if (newIndex > 0)
          {
               newIndex--;
               
               if (doIt)
               {
                    selectRow(newIndex);
               }
          }
          
          return newIndex;
     }
     
     
     /**
      * Vybere řádek a případně provede scrool
      * @param newRowIndex index řádku k výběru
      */
     void selectRow(int newRowIndex)
     {
          selectRow(newRowIndex, true);
     }
     
     /**
      * Vybere řádek a případně provede scrool
      * @param newRowIndex index řádku k výběru
      * @param doScrool true přesune na pozici vybraného řádku
      */
     void selectRow(int newRowIndex, boolean doScroll)
     {
     
          Rectangle repaintRect = table.getCellRect(selectedRowIndex, 0, doScroll); //Předpokládáme pouze 1. sloupec !!!
          table.repaint(repaintRect);
          
          selectedRowIndex = newRowIndex;
          repaintRect = table.getCellRect(newRowIndex, 0, doScroll);
          table.repaint(repaintRect);
          
          
          return;
     }
     
     /**
      * Zjistí vybraný řádek
      */
     private int getSelectedRowIndex()
     {
          int numRows = table.getRowCount();
          
          if (selectedRowIndex >= numRows && numRows > 0)
          {
               selectedRowIndex = numRows - 1;
          }
          
          return selectedRowIndex;
          
     }
     
     /**
      * Zjistí, zda u daného řádku lze měnit velikost
      * @param row řádek k testování
      * @param table (nevyužito)
      * @return true, pukud je řádek >=0
      */
     private static boolean canResize(int row, JTable table)
     {
          return (row >= 0);
     }
     
     /**
      * Změní výšku řádku jak v rowheaderu, tak v hlavní tabulce.
      * Pokud tabulka využívá <code>AttributiveCellTableModel</code>, je výška v pixelech zapsána i do něj.
      * @param resizingRow index řádku
      * @param newHeight   nová výška řádku
      * @return 0
      */
     private int changeRowHeight(int resizingRow, int newHeight)
     {
     
          if (newHeight < 1) newHeight = 1; //Nutná pojistka na minimální výšku !!!
          
          table.setRowHeight(resizingRow, newHeight); //nastaví výšku řádku RowHeaderu
          
          if ((mainTable != null) &&  resizingRow < mainTable.getRowCount())
          {
               mainTable.setRowHeight(resizingRow, newHeight); //výška řádku hlavní tabulky
               mainTable.requestFocus();
               
               //přístup k modelu
               TableModel model = mainTable.getModel();
               
               if (model instanceof AttributiveCellTableModel)
               {
                    CellAttribute cellAtt = ((AttributiveCellTableModel)model).getCellAttribute();
                    cellAtt.setHeight(resizingRow, newHeight, cellAtt.SIZE_PIX);
               }
          }
          
          return 0;
     }
     
     /**
      * Přenese nastavení výšek z rowheaderu do hlavní tabulky.
      * @return 0
      */
     public int setMainTableHeights()
     {
     
          int count = Math.min(table.getRowCount(), mainTable.getRowCount());
          
          if (mainTable == null) return 0;
          
          for (int i = 0; i < count; i++)
          {
               int newHeight = table.getRowHeight(i);
               
               if (newHeight < 1) newHeight = 1; //Nutná pojistka na minimální výšku !!!
               
               mainTable.setRowHeight(i, newHeight); //výška řádku hlavní tabulky
          }
          
          return 0;
     }
     
}  // End of RowHeaderTableUI

